home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / language / awe / awe-full.lha / Awe2 / DoNotUseThisSrc / MultiCpuMux.cc < prev    next >
C/C++ Source or Header  |  1990-08-08  |  14KB  |  585 lines

  1. // This may look like C code, but it is really -*- C++ -*-
  2. // 
  3. // Copyright (C) 1988 University of Illinois, Urbana, Illinois
  4. //
  5. // written by Dirk Grunwald (grunwald@cs.uiuc.edu)
  6. //
  7.  
  8. #ifdef __GNUG__
  9. #  pragma implementation
  10. #endif
  11.  
  12. #include "MultiCpuMux.h"
  13. #include "CpuMultiplexorP.h"
  14. #include "SpinLock.h"
  15. #include "SpinBarrier.h"
  16. #include "SpinFetchAndOp.h"
  17. #include "Thread.h"
  18. #include "HardwareContextP.h"
  19. #include "ThreadContainer.h"
  20. #include "ReserveByException.h"
  21. #include "Pragma.h"
  22. #include <math.h>
  23.  
  24. //
  25. //    Things left to do:
  26. //
  27. //    + Make the ThreadHeap really use the Gnu PairingHeap structure.
  28. //      Doug Lea added an iterator class & other enhancements.
  29. //
  30. //    + Capture signals, transfer them to an Exception class. Can
  31. //      use this to implement time-slices & the like, as well as....
  32. //
  33. //    + Put in *addCpu* and *removeCpu* calls to CpuMultiplexor.
  34. //      This would allow run-time addition/removal of CPUS, so
  35. //      you can tailor your program to system 
  36. //        This is tricky. Should probably do it when you
  37. //        advance the clock, but it'll be tricky to get all
  38. //        the CPUs to agree on the barrier height for the
  39. //        rendezvous. Also might complicate the *distinct
  40. //        pools of threads per cpu*.
  41. //
  42.  
  43. static SpinLock CpuMultiplexorsLock;
  44.  
  45. static SpinFetchAndOp GlobalCurrentEventsCounter(0);
  46. static SpinLock GivingUpLock;
  47. static int GivingUpCounter = 0;
  48. static int GivingUpGeneration = 0;
  49.  
  50. //
  51. //    A currentEvents pile for each processor. The count is only correct
  52. //    if youve reserved the spin lock -- its used as a guess.
  53. //
  54. static SpinLock CurrentEventsLock[MaxCpuMultiplexors];
  55. static int CurrentEventsCounter[MaxCpuMultiplexors];
  56. static ThreadContainer *CurrentEvents[MaxCpuMultiplexors];
  57.  
  58. //
  59. //    This can not be private, or we wont see all the action
  60. //
  61.  
  62. MultiCpuMux::MultiCpuMux(int debug) : (debug)
  63. {
  64.     pNameTemplate = "MultiCpuMux";
  65.     sprintf(nameSpace, "[%s-%d] ", pNameTemplate, iYam);
  66.     CpuMuxDebugFlag = debug;
  67. }
  68.  
  69. MultiCpuMux::~MultiCpuMux()
  70. {
  71. }
  72.  
  73. //
  74. // Add a single CPU to a set of current CPUs. There is an advantage of
  75. // having all child processes be spawned by CPU #0; all child signals
  76. // will be caught by the single parent.
  77. //
  78. // This entry is called by a Thread.
  79. //
  80. void MultiCpuMux::enrollCpu()
  81. {
  82.     //
  83.     // move thread to master process. There's a distinct possibility
  84.     // that this guy will get stolen from Cpu #0 if everyone else is
  85.     // looking for work.
  86.     //
  87.     while (iYam != 0) {
  88.     currentThread -> cpuAffinity = 0;
  89.     relocateException.cpu(0);
  90.     raise( &relocateException );
  91.     }
  92.     //
  93.     // If we're only using a single 
  94.     //
  95.     //
  96.     // raise an exception to do the actual fork. This means that
  97.     // control flow for the new child process will be in the
  98.     // stirItAround loop, as opposed to here.
  99.     //
  100.     enrollDismissCpuException.enroll();
  101.     raise( &enrollDismissCpuException );
  102.     currentThread -> cpuAffinity = -1;
  103. }
  104.  
  105. void
  106. MultiCpuMux::dismissCpu()
  107. {
  108.     assert(0);
  109. }
  110.  
  111. void
  112. MultiCpuMux::allocateLocalEventStructures(int newIYam, int outOf)
  113. {
  114. #ifndef NDEBUG
  115.     if (CpuMuxDebugFlag) {
  116.     CpuCerrLock.reserve();
  117.     cerr << name() << "Allocate CpuMux structures for " << newIYam << "\n";
  118.     CpuCerrLock.release();
  119.     }
  120. #endif /* NDEBUG */
  121.  
  122.     iYam = newIYam;
  123.     sprintf(nameSpace, "[%s-%d] ", pNameTemplate, iYam);
  124.     pName = nameSpace;
  125.  
  126.     globalCurrentEventsCounter = &GlobalCurrentEventsCounter;
  127.  
  128.     CurrentEventsCounter[iYam] = 0;
  129.     CurrentEvents[iYam] = AllocateHardwareCurrentEventsStructure();
  130.  
  131.     myCurrentEvents = CurrentEvents[iYam];
  132.     myCurrentEventsLock = &CurrentEventsLock[iYam];
  133.     myCurrentEventsCounter = &CurrentEventsCounter[iYam];
  134.  
  135. #ifndef NDEBUG
  136.     if (CpuMuxDebugFlag) {
  137.     CpuCerrLock.reserve();
  138.     cerr << name() << "set CpuMultiplexors to " << outOf << "\n";
  139.     CpuCerrLock.release();
  140.     }
  141. #endif /* NDEBUG */
  142.  
  143.     CpuMultiplexorsLock.reserve();
  144.     CpuMultiplexors = outOf;
  145.  
  146.     GivingUpLock.reserve();
  147.     if (GivingUpCounter >= CpuMultiplexors) {
  148.     GivingUpGeneration++;
  149.     GivingUpCounter = 0;
  150.     }
  151.     GivingUpLock.release();
  152.  
  153.     CpuMultiplexorsLock.release();
  154. }
  155.  
  156. void
  157. MultiCpuMux::allocateEventStructures(int newIYam, int outOf)
  158. {
  159.     allocateLocalEventStructures(newIYam, outOf);
  160. }
  161.  
  162. void
  163. MultiCpuMux::deallocateEventStructures()
  164. {
  165. #ifndef NDEBUG
  166.     if (CpuMuxDebugFlag) {
  167.     CpuCerrLock.reserve();
  168.     cerr << name() << "Deallocate CpuMux structures for " << iYam << "\n";
  169.     CpuCerrLock.release();
  170.     }
  171. #endif /* NDEBUG */
  172.  
  173.     myCurrentEventsLock -> reserve();
  174.     //
  175.     // Move remaining events to another queue. We're not adding new events,
  176.     // just moving them around, so we don't increase GlobalCurrentEventsCounter
  177.     //
  178.     while ( CurrentEventsCounter[iYam] > 0 ) {
  179.     CurrentEventsLock[0].reserve();
  180.     assert(CurrentEvents[0] != 0);
  181.     while( ! myCurrentEvents -> isEmpty() )  {
  182.         CurrentEvents[0] -> add( myCurrentEvents -> remove() );
  183.         CurrentEventsCounter[0]++;
  184.         CurrentEventsCounter[iYam]--;
  185.     }
  186.     CurrentEventsLock[0].release();
  187.     }
  188.  
  189.     CpuMultiplexorsLock.reserve();
  190.     CpuMultiplexors--;
  191.  
  192.     GivingUpLock.reserve();
  193.     if (GivingUpCounter >= CpuMultiplexors) {
  194.     GivingUpGeneration++;
  195.     GivingUpCounter = 0;
  196.     }
  197.     GivingUpLock.release();
  198.  
  199.     CpuMultiplexorsLock.release();
  200.  
  201. #ifndef NDEBUG
  202.     if (CpuMuxDebugFlag) {
  203.     CpuCerrLock.reserve();
  204.     cerr << name() << "set CpuMultiplexors to " << CpuMultiplexors;
  205.     cerr << " and trigger GivingUp\n";
  206.     CpuCerrLock.release();
  207.     }
  208. #endif /* NDEBUG */
  209.  
  210.     delete myCurrentEvents;
  211.     myCurrentEvents = 0;
  212.     CurrentEvents[iYam] = 0;
  213.     CurrentEventsCounter[iYam] = 0;
  214.     myCurrentEventsLock -> release();
  215. }
  216.  
  217. void
  218. MultiCpuMux::fireItUp(int cpus, unsigned shared)
  219. {
  220.     assert(cpus > 0);
  221.     
  222.     if ( cpus > MaxCpuMultiplexors ) {
  223.     cpus = MaxCpuMultiplexors;
  224.     }
  225.  
  226. #ifndef NDEBUG
  227.     if (CpuMuxDebugFlag) {
  228.     CpuCerrLock.reserve();
  229.     cerr << name() << "Allocate " << shared << " bytes of shared memory\n";
  230.     CpuCerrLock.release();
  231.     }
  232. #endif /* NDEBUG */
  233.  
  234.     if ( cpus > 1 ) {
  235.     extern void SharedMemoryInit( unsigned );
  236.     SharedMemoryInit( shared );
  237.     }
  238.  
  239.     warmThePot(cpus);
  240.     stirItAround();
  241.     coolItDown();
  242. }
  243.  
  244. void
  245. MultiCpuMux::warmThePot(int cpus)
  246. {
  247.     assert(cpus > 0);
  248.     
  249.     if ( cpus > MaxCpuMultiplexors ) {
  250.     cpus = MaxCpuMultiplexors;
  251.     }
  252.  
  253.     CpuMultiplexors = cpus;
  254.     enabled = 1;
  255.  
  256.     //
  257.     //    Spawn the children, giving each a unique number from 0..(cpus-1).
  258.     //  The first child gets id (cpus-1), and the original process gets 0.
  259.     //
  260.  
  261.     iYam = 0;
  262. #ifndef NDEBUG
  263.     if (CpuMuxDebugFlag) {
  264.     CpuCerrLock.reserve();
  265.     cerr << name() << "Allocate " << CpuMultiplexors << " cpus\n";
  266.     CpuCerrLock.release();
  267.     }
  268. #endif /* NDEBUG */
  269.  
  270.     for (int whoAmI = 1; whoAmI < CpuMultiplexors; whoAmI++) {
  271.     if (iYam == 0) {
  272.         int pid = fork();
  273.         if (pid == 0) {    // child 
  274.         sleep(10);
  275.         allocateEventStructures(whoAmI, CpuMultiplexors);
  276.         break;
  277.         }
  278.     }
  279.     }
  280.     pid = getpid();
  281. #ifndef NDEBUG
  282.     if (CpuMuxDebugFlag) {
  283.     CpuCerrLock.reserve();
  284.     cerr << name() << "I am now id " << iYam << " and pid " << pid <<" \n";
  285.     CpuCerrLock.release();
  286.     }
  287. #endif /* NDEBUG */
  288. }
  289.  
  290. void
  291. MultiCpuMux::coolItDown()
  292. {
  293.     if (iYam > 0) {
  294. #ifndef NDEBUG
  295.     if (CpuMuxDebugFlag) {
  296.         CpuCerrLock.reserve();
  297.         cerr << name() << "exit\n";
  298.         CpuCerrLock.release();
  299.     }
  300. #endif
  301.     deallocateEventStructures();
  302.     _exit(0);
  303.     }
  304.     else {
  305.     //
  306.     //    reap the dead children. This way we know they are all dead.
  307.     //    The caller can then safely exit.
  308.     //
  309.     while (CpuMultiplexors > 1) {
  310.         int pid = wait(0);
  311.         if (pid == -1) {
  312.         perror("wait");
  313.         break;
  314.         }
  315.     }
  316.     //
  317.     //  In case of break in above loop
  318.     //
  319.     CpuMultiplexors = 1;
  320.     }
  321. }
  322.  
  323. void
  324. MultiCpuMux::add(Thread *who)
  325. {
  326. #ifndef NDEBUG
  327.     if (CpuMuxDebugFlag) {
  328.     CpuCerrLock.reserve();
  329.     if (who != 0 && who -> name() != 0) {
  330.         cerr << name() << " add " << who -> name() << "\n";
  331.     } else {
  332.         cerr << name() << " add " << hex(long(who)) << "\n";
  333.     }
  334.     CpuCerrLock.release();
  335.     }
  336. #endif /* NDEBUG */
  337.     myCurrentEventsLock -> reserve();
  338.     addUnlocked( who );
  339.     (*myCurrentEventsCounter)++;
  340.     myCurrentEventsLock -> release();
  341.  
  342.     GlobalCurrentEventsCounter.add(1);
  343. }
  344.  
  345. void
  346. MultiCpuMux::addToAnother(int cpu, Thread *who)
  347. {
  348.     assert( cpu >= 0 && cpu < CpuMultiplexors );
  349.     CurrentEventsLock[cpu].reserve();
  350.     CurrentEvents[cpu] -> add( who );
  351.     CurrentEventsCounter[cpu]++;
  352.     CurrentEventsLock[cpu].release();
  353.  
  354.     GlobalCurrentEventsCounter.add(1);
  355. }
  356.  
  357. Thread *
  358. MultiCpuMux::remove()
  359. {
  360.     //
  361.     // Check to see if there is a current event, either in our current
  362.     // events queue or someone elses current events queue. If there is
  363.     // nothing, return 0.
  364.     //
  365.     
  366.     Thread *threadToExecute = 0;
  367.     
  368.     //
  369.     //    System stopped?
  370.     //
  371.     if (*terminated) {
  372. #ifndef NDEBUG
  373.     if (CpuMuxDebugFlag) {
  374.         CpuCerrLock.reserve();
  375.         cerr << name() << " Stopping muxing \n" ;
  376.         CpuCerrLock.release();
  377.     }
  378. #endif /* NDEBUG */
  379.     return(0);
  380.     }
  381.     
  382.     //
  383.     //    I got something to do?
  384.     //
  385.     
  386.     myCurrentEventsLock -> reserve();
  387.     
  388.     if ( *myCurrentEventsCounter > 0 ) {
  389.     threadToExecute = myCurrentEvents -> remove();
  390.     (*myCurrentEventsCounter) --;
  391.     }
  392.     
  393.     myCurrentEventsLock -> release();
  394.     
  395.     //
  396.     //    Maybe someone else has something to do?
  397.     //
  398.     if ( threadToExecute == 0 && GlobalCurrentEventsCounter.value() > 0 ) {
  399.     int ask = iYam;
  400.     do {
  401.         ask++;                // start with next person,
  402.         if ( ask >= CpuMultiplexors ) {    // wrap around for fairness
  403.         ask = 0;
  404.         }
  405. #ifndef NDEBUG
  406.         if (CpuMuxDebugFlag) {
  407.         CpuCerrLock.reserve();
  408.         cerr << name() << "Ask " << ask << " about events \n";
  409.         CpuCerrLock.release();
  410.         }
  411. #endif /* NDEBUG */
  412.         //
  413.         // Note that were *not* locking before looking
  414.         // at CurrentEventsCount -- we treat this as a *guess*
  415.         // before bothering to lock on it. Admittedly, this could
  416.         // cause a problem, so maybe the second time around,
  417.         // we should always reserve and then look. 
  418.         //
  419.         if ( CurrentEventsCounter[ask] > 0) {
  420.         CurrentEventsLock[ask].reserve();
  421.         if ( CurrentEventsCounter[ask] > 0) {
  422. #ifndef NDEBUG
  423.             if (CpuMuxDebugFlag) {
  424.             CpuCerrLock.reserve();
  425.             cerr << name();
  426.             cerr << "Found one in " << ask << "\n";
  427.             CpuCerrLock.release();
  428.             }
  429. #endif /* NDEBUG */
  430.             threadToExecute = CurrentEvents[ask] -> remove();
  431.             //
  432.             // Check that this thread isnt trying to get to
  433.             // a specific CPU.
  434.             //
  435.             if (threadToExecute -> cpuAffinity > 0 &&
  436.             threadToExecute -> cpuAffinity != iYam) {
  437. #ifndef NDEBUG
  438.                 if (CpuMuxDebugFlag) {
  439.                 CpuCerrLock.reserve();
  440.                 cerr << name();
  441.                 cerr << "but returned it because of afinity\n";
  442.                 CpuCerrLock.release();
  443.                 }
  444. #endif /* NDEBUG */
  445.                 CurrentEvents[ask] -> add(threadToExecute);
  446.                 threadToExecute = 0;
  447.             }
  448.             else {
  449.             CurrentEventsCounter[ask]--;
  450.             }
  451.         }
  452.         CurrentEventsLock[ask].release();
  453.         }
  454.     } while (ask != iYam && threadToExecute == 0);
  455.     }
  456.     
  457. #ifndef NDEBUG
  458.     if (CpuMuxDebugFlag) {
  459.     CpuCerrLock.reserve();
  460.     cerr << name() << "find ";
  461.     if (threadToExecute == 0) {
  462.         cerr << "nothing\n";
  463.     } else {
  464.         cerr << threadToExecute -> name() << "\n";
  465.     }
  466.     CpuCerrLock.release();
  467.     }
  468. #endif /* NDEBUG */
  469.  
  470.     if ( threadToExecute != 0 ) {
  471.     GlobalCurrentEventsCounter.add(-1);
  472.     }
  473.     
  474.     return( threadToExecute );
  475. }
  476.  
  477. //
  478. // This is the job dispatcher.
  479. //
  480.  
  481. void
  482. MultiCpuMux::stirItAround()
  483. {
  484.     currentThread = 0;
  485.  
  486.     if (!enabled) {
  487.     cerr << "Need to initialize CpuMultiplexor before using it\n";
  488.     }
  489.     
  490.     while( ! *terminated ) {
  491.     while ( currentThread == 0 ) {
  492.         
  493.         currentThread = remove();
  494.         
  495.         if (currentThread != 0) {
  496.         break;
  497.         }
  498.         
  499.         GivingUpLock.reserve();
  500.         
  501.         GivingUpCounter++;
  502.         
  503.         assert( GivingUpCounter > 0 && GivingUpCounter <= CpuMultiplexors);
  504.         
  505.         if ( GivingUpCounter == CpuMultiplexors
  506.         && GlobalCurrentEventsCounter.value() == 0)
  507.         {
  508.         
  509.         GivingUpGeneration ++;
  510.         GivingUpCounter = 0;
  511.         GivingUpLock.release();
  512.         
  513. #ifndef NDEBUG
  514.             if (CpuMuxDebugFlag) {
  515.             CpuCerrLock.reserve();
  516.             cerr << name() << "give up\n";
  517.             CpuCerrLock.release();
  518.             }
  519. #endif /* NDEBUG */
  520.  
  521.         return;
  522.         }
  523.         else {
  524.         VOLATILE int generation = GivingUpGeneration;
  525.         VOLATILE int *genp = &GivingUpGeneration;
  526.         
  527.         GivingUpLock.release();
  528.         
  529.         while( generation == *genp
  530.               && GlobalCurrentEventsCounter.value() == 0
  531.               && !*terminated );
  532.  
  533.         GivingUpLock.reserve();
  534.         if ( *genp != generation || *terminated ) {
  535. #ifndef NDEBUG
  536.             if (CpuMuxDebugFlag) {
  537.             CpuCerrLock.reserve();
  538.             cerr << name() << " giving up\n";
  539.             CpuCerrLock.release();
  540.             }
  541. #endif /* NDEBUG */
  542.             GivingUpLock.release();
  543.             return;
  544.         }
  545.         else {
  546.             GivingUpCounter--;
  547.             assert(GivingUpCounter >= 0);
  548.             GivingUpLock.release();
  549. #ifndef NDEBUG
  550.             if (CpuMuxDebugFlag) {
  551.             CpuCerrLock.reserve();
  552.             cerr << name() << " check for something";
  553.             cerr << " i have " << *myCurrentEventsCounter;
  554.             cerr << " out of " ;
  555.             cerr << GlobalCurrentEventsCounter.value() << "\n";
  556.             CpuCerrLock.release();
  557.             }
  558. #endif /* NDEBUG */
  559.         }
  560.         }
  561.     }
  562.     
  563. #ifndef NDEBUG
  564.     if (CpuMuxDebugFlag || currentThread -> debug()) {
  565.         CpuCerrLock.reserve();
  566.         cerr << name() << " switch to ";
  567.         cerr << currentThread->name() << "\n";
  568.         CpuCerrLock.release();
  569.     }
  570. #endif /* NDEBUG */
  571.  
  572. #ifdef DEBUG_MALLOC
  573.     assert( malloc_verify() );
  574. #endif DEBUG_MALLOC
  575.     systemContext.switchContext(&(currentThread -> pContext));
  576. #ifdef DEBUG_MALLOC
  577.     assert( malloc_verify() );
  578. #endif DEBUG_MALLOC
  579.  
  580.     assert(raisedBy != 0);
  581.     raisedBy -> handleException();
  582.     raisedBy = 0;
  583.     }
  584. }
  585.